<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <!-- 定义当前 HTML 页面所使用的字符集 -->
    <meta charset="UTF-8" />
    <!-- 针对 IE 浏览器的一个特殊配置，含义是让 IE 浏览器以最高的渲染级别来渲染页面 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <!-- 开启理想视口，并禁用缩放功能，强制文档与设备的宽度保持 1:1 -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>Canvas 动态时钟效果</title>
    <style>
      .flex-center {
        display: flex;
        justify-content: center;
        align-items: center;
      }
      body {
        margin: 0;
      }
      .app-container {
        position: fixed;
        width: 100%;
        height: 100%;
        background-image: linear-gradient(135deg, #224141, #162a2a);
      }
    </style>
  </head>
  <body>
    <div class="app-container flex-center">
      <canvas id="canvas"></canvas>
    </div>

    <script>
      let timer = 0
      const cvs = document.querySelector('#canvas')
      const ctx = cvs.getContext('2d')
      const canvasSize = 400
      cvs.width = canvasSize
      cvs.height = canvasSize
      const radius = cvs.width / 2
      const rem = cvs.width / (canvasSize / 2)

      /** 绘制时钟的盘面、圆点和数字 */
      function drawBackground() {
        ctx.save()
        ctx.translate(radius, radius)
        ctx.lineWidth = 10 * rem
        ctx.beginPath()
        ctx.arc(0, 0, radius - ctx.lineWidth / 2, 0, 2 * Math.PI)
        ctx.stroke()
        ctx.beginPath()
        ctx.arc(0, 0, radius - ctx.lineWidth, 0, 2 * Math.PI)
        ctx.fillStyle = '#ffffff'
        ctx.fill()

        const hourNumbers = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2]
        ctx.font = 18 * rem + 'px Arial'
        ctx.textAlign = 'center'
        ctx.textBaseline = 'middle'
        hourNumbers.forEach((num, index) => {
          const rad = ((2 * Math.PI) / 12) * index
          const x = Math.cos(rad) * (radius - 30 * rem)
          const y = Math.sin(rad) * (radius - 30 * rem)
          ctx.fillStyle = '#000000'
          ctx.fillText(num.toString(), x, y)
        })

        for (let i = 0; i < 60; i++) {
          const rad = ((2 * Math.PI) / 60) * i
          const x = Math.cos(rad) * (radius - 18 * rem)
          const y = Math.sin(rad) * (radius - 18 * rem)
          ctx.beginPath()
          ctx.fillStyle = i % 5 === 0 ? '#000000' : '#ccc'
          ctx.arc(x, y, 2 * rem, 0, 2 * Math.PI)
          ctx.fill()
        }
      }

      /** 绘制时钟的时针 */
      function drawHour(hour, minute) {
        ctx.save()
        ctx.beginPath()
        const rad = ((2 * Math.PI) / 12) * hour
        var minuteRad = ((2 * Math.PI) / 12 / 60) * minute
        ctx.rotate(rad + minuteRad)
        ctx.lineWidth = 6 * rem
        ctx.lineCap = 'round'
        ctx.moveTo(0, 10 * rem)
        ctx.lineTo(0, -radius / 2)
        ctx.stroke()
        ctx.restore()
      }

      /** 绘制时钟的分针 */
      function drawMinute(minute) {
        ctx.save()
        const rad = ((2 * Math.PI) / 60) * minute
        ctx.rotate(rad)
        ctx.lineWidth = 3 * rem
        ctx.lineCap = 'round'
        ctx.beginPath()
        ctx.moveTo(0, 10 * rem)
        ctx.lineTo(0, -radius + 30 * rem)
        ctx.stroke()
        ctx.restore()
      }

      /** 绘制时钟的秒针 */
      function drawSecond(second) {
        ctx.save()
        var rad = ((2 * Math.PI) / 60) * second
        ctx.rotate(rad)
        ctx.fillStyle = '#c14543'
        ctx.beginPath()
        ctx.moveTo(-2 * rem, 20 * rem)
        ctx.lineTo(2 * rem, 20 * rem)
        ctx.lineTo(1, -radius + 18 * rem)
        ctx.lineTo(-1, -radius + 18 * rem)
        ctx.fill()
        ctx.restore()
      }

      /** 绘制时钟中心的白点 */
      function drawDot() {
        ctx.save()
        ctx.fillStyle = '#fff'
        ctx.beginPath()
        ctx.arc(0, 0, 3 * rem, 0, 2 * Math.PI)
        ctx.fill()
        ctx.restore()
      }

      /** 绘制 */
      function draw() {
        ctx.clearRect(0, 0, cvs.width, cvs.height)
        const now = new Date()
        const hour = now.getHours()
        const minute = now.getMinutes()
        const second = now.getSeconds()
        drawBackground()
        drawHour(hour, minute)
        drawMinute(minute)
        drawSecond(second)
        drawDot()
        ctx.restore()

        window.requestAnimationFrame(draw)
      }

      draw()
    </script>
  </body>
</html>
